#coding=utf8
import os
import re
import sys
import logging
import datetime

from cronic.utils import merge_maps
from cronic.exceptions import *


PREDEFINES = {
    'CWD': os.path.os.getcwd(),
}


class Line(object):
    def __init__(self, lineno, source):
        self.lineno = lineno
        self.source = source
        self.text = source

    def __repr__(self):
        return '<{}: {}>'.format(self.lineno, self.source)

    def set_text(self, text):
        self.text = text
        return self

    def clone(self, text):
        return Line(self.lineno, self.source).set_text(text)


class CronfileParser(object):
    def __init__(self, cli_kwargs, predefines):
        self.cli_kwargs = cli_kwargs
        self.predefines = predefines

        self.global_vars = {}
        self.backlog_when = None
        self.backlog_what = []
        self.outputs = []

        self.declare_closed = False
        self.render_ctx = {}

    def handle_line(self, line):
        if re.match(r'^\s*$', line.text):
            return

        if re.match(r'\s*#', line.text):
            self.handle_comment(line)
            return

        if re.match(r'\w+=.+$', line.text):
            self.handle_declare(line)
            return

        if re.match(r'with\s+[\'"\*/\d\s,]+:\s*$', line.text):
            self.handle_when(line)
            return

        if re.match(r'\s+', line.text):
            self.handle_what(line)
            return
        else:
            raise CompileError(u'解析失败 [L:{}]: {}'.format(line.lineno, line.source))

    def handle_comment(self, line):
        pass

    def handle_declare(self, line):
        if self.declare_closed:
            raise CompileError('必须在头部声明变量 [L:{}]: {}'.format(line.lineno, line.text))

        k, v = line.text.split('=', 1)
        self.global_vars[k.strip()] = v.strip().strip('\'"')

    def close_declare(self):
        self.declare_closed = True
        self.render_ctx = merge_maps(self.cli_kwargs, self.global_vars, self.predefines)

    def handle_when(self, line):
        if not self.declare_closed:
            self.close_declare()

        self.close_block()

        when = re.split(r'\s', line.text, 1)[1].split(':')[0].strip().strip('\'"')
        when = re.sub(r'\s+', ' ', when)
        if not re.match(r'([\*/\d]+ ){4}[\*/\d]+', when):
            raise CompileError(u'时间格式错误 [L:{}]: {}'.format(line.lineno, line.source))

        self.backlog_when = line.clone(when)

    def handle_what(self, line):
        try:
            cmd = line.text.format(**self.render_ctx)
        except KeyError as ex:
            raise CompileError(u'上下文变量查找失败 [L:{}]: {}, ex={}'.format(line.lineno, line.source, ex))

        self.backlog_what.append(line.clone(cmd))

    def close_block(self):
        if self.backlog_when is None:
            return

        if len(self.backlog_what) == 0:
            raise CompileError(u'空声明 [L:{}]: {}'.format(self.backlog_when.lineno, self.backlog_when.source))

        for line in self.backlog_what:
            self.outputs.append((self.backlog_when, line))

        self.backlog_when = None
        self.backlog_what = []


class Cronfile(object):
    def __init__(self, cli_kwargs, fpath):
        self.cli_kwargs = cli_kwargs or {}
        self.fpath = fpath

        self.global_vars = {}
        self.outputs = []
        self.segment = None

    def parse(self):
        parser = CronfileParser(self.cli_kwargs, PREDEFINES)

        try:
            with open(self.fpath) as f:
                lineno = 1
                for row in f:
                    line = Line(lineno, row.strip('\n'))
                    parser.handle_line(line)
                    lineno += 1
        except IOError as e:
            logging.getLogger(__name__).error(u'打开文件 {} 失败：{}'.format(fname, e))
            sys.exit(-1)

        parser.close_block()

        self.global_vars = parser.global_vars
        self.outputs = parser.outputs
        self.segment = CronSegment(self.global_vars.get('SEGMENT') or os.path.basename(self.fpath))

    def generate(self):
        now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')

        yield self.segment.begin_line
        yield '### from file: {}, at {}'.format(os.path.abspath(self.fpath), now)
        yield ''
        for when, what in self.outputs:
            yield '{} {}  # L:{}'.format(when.text.strip(), what.text.strip(), what.lineno).replace('%', '\\%')
            yield ''

        yield self.segment.end_line
        yield ''


class CronSegment(object):
    BEGIN_TEMPLATE = '\n### !!!DON\'T EDIT!!! AUTO GENERATED CRONIC SEGMENT {} ###'
    END_TEMPLATE = '### END CRONIC SEGMENT {} ###\n'

    def __init__(self, segment_name):
        self.segment_name = segment_name

        self.begin_line = self.BEGIN_TEMPLATE.format(self.segment_name)
        self.end_line = self.END_TEMPLATE.format(self.segment_name)

        self.regex = self.begin_line.replace('.', '\\.') + r'.*?' + self.end_line.replace('.', '\\.')
